package com.yy.micro.magic.core.model;

import com.yy.micro.magic.core.config.Content;
import com.yy.micro.magic.core.context.MagicCubeContext;
import com.yy.micro.magic.core.domain.MagicResponse;
import com.yy.micro.magic.core.result.ResultStrategy;
import com.yy.micro.magic.exception.ExceptionManager;
import com.yy.micro.magic.utils.JsonUtils;
import com.yy.micro.magic.utils.MapUtils;

import org.springframework.util.StringUtils;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

public class ResultCollector {
    private final Map<String, Object> results = new HashMap<>();
    private Map<String, Object> responseResults = new HashMap<>();



    private final Map<String, Map<String, Object>> resultsTemp = new HashMap<>();

//    @Deprecated
//    private Map<String, Object> tempVariablePool = new HashMap<String, Object>();// 临时变量池，在映射过程中可当全局变量使用，用于计数等操作
//
//    @Deprecated
//    private List<String> cleaners;
//
//    @Deprecated
//    private String keyCleaner;

    private ResultStrategy<Map<String,Object>> resultStrategy;

    private ExceptionManager exceptionManager;

    private MagicCubeContext context;

    public ResultCollector(){

    }

    public ResultCollector(MagicCubeContext context){
        setContext(context);
    }
    /**
     * 根据Key获取结果偏大中的节点位置。
     *
     * @param key rule key
     * @return result
     */
    public Map<String, Object> getResult(final String key) {
        Map<String, Object> result = resultsTemp.get(key);
        if (null == result) {// 如果resultsTemp中没有key，则新建结果节点
            result = addResultMap(key, null);
            resultsTemp.put(key, result);
        }
        return result;
    }

    /**
     * 向结果集中新增一个节点
     *
     * @param key
     *            节点路径
     * @param map
     *            节点map
     * @return result
     */
    public Map<String, Object> addResultMap(final String key, final Map<String, Object> map) {
        final String[] keys = MapUtils.splitKey(key);
        return addResultMap(keys, map);
    }

    /**
     * 向结果集中新增一个结果节点，路径为keys
     *
     * @param keys
     *            路径数据
     * @param map
     *            节点
     * @return result
     */
    @SuppressWarnings("unchecked")
    public Map<String, Object> addResultMap(final String[] keys, final Map<String, Object> map) {
        Map<String, Object> temp = results;
        final String[] ks = new String[keys.length - 1];
        System.arraycopy(keys, 0, ks, 0, ks.length);// 去掉最末尾的叶子路径节点
        for (int i = 0; i < ks.length; i++) {
            final String key = ks[i];
            if (key.contains(Content.KEY_LIST_INDENTIFIER)) {// 如果路径中包含下标
                final String[] str = MapUtils.splitKey(key, Content.KEY_LIST_INDENTIFIER);// 分解下标
                if (temp.containsKey(str[0])) {// 结果集中已包含该节点
                    final Map<String, Object> list = (Map<String, Object>) temp.get(str[0]);
                    if (list.containsKey(str[1])) {
                        temp = (Map<String, Object>) list.get(str[1]);
                    } else {
                        final Map<String, Object> m = new HashMap<String, Object>();
                        list.put(str[1], m);
                        temp = m;
                    }

                } else {// 结果集中不包含该节点，List用Treemap替代，用ArrayList会出现越界情况。
                    // final List<Object> list = new ArrayList<Object>(8);
                    final Map<String, Object> list = new TreeMap<String, Object>();
                    temp.put(str[0], list);
                    final Map<String, Object> m = new HashMap<String, Object>();
                    list.put(str[1], m);
                    temp = m;
                }
            } else {
                if (temp.containsKey(key)) {
                    temp = (Map<String, Object>) temp.get(key);
                } else {
                    final Map<String, Object> m = new HashMap<String, Object>();
                    temp.put(key, m);
                    temp = m;
                }
            }
        }
        return temp;
    }

    /**
     * 获取映射后的结果，并把结果集中的{@link TreeMap}转换为{@link List}
     *
     * @return
     */
    public Map<String, Object> getResult() {
        final Map<String, Object> map = MapUtils.treeMapToList(results);
        return map;
    }

    public Map<String,Object> getResponseResult(boolean force){
        if (null==responseResults || force){
            responseResults = MapUtils.treeMapToList(results);
        }
        if (null !=resultStrategy){
            responseResults = resultStrategy.applay(responseResults);
        }
        return responseResults;
    }

    /**
     * @return the results
     */
    public Map<String, Object> getResults() {
        return results;
    }
//
//    /**
//     * 映射过程中，对某一共同特点的值
//     * 做中间缓存时，如处理千分位时，可把所有的千分位处理节点保存为一个List。该方法适用一对多的场景，即一个Key对应多个值
//     *
//     * @param key
//     * @param value
//     */
//    @SuppressWarnings("unchecked")
//    @Deprecated
//    public <T> void addListTempVariable(final String key, final T value) {
//        List<T> values = null;
//        if (contains(key)) {
//            values = getTempVariableValue(key, List.class);
//        } else {
//            values = new ArrayList<T>();
//            addTempVariable(key, values);
//        }
//        values.add(value);
//    }
//
//    /**
//     * 添加缓存变量，在映射过程中，有些值 需要几过多次处理，缓存的作用是存放中间变量，每次处理完后，结果存在缓存中，所有的处理结束后，再保存到结果集中。
//     *
//     * @param key
//     * @param value
//     */
//    @Deprecated
//    public void addTempVariable(final String key, final Object value) {
//        tempVariablePool.put(key, value);
//    }
//
//    /**
//     * 判断key是否已经在当前的临时变量池中
//     *
//     * @param key
//     * @return
//     */
//    @Deprecated
//    public boolean contains(final String key) {
//        return tempVariablePool.containsKey(key);
//    }
//
//    /**
//     * 从临时变量池中取出已经存在的value，并转换为指定的类型
//     *
//     * @param key
//     * @param clazz
//     *            转换为指定类型
//     * @return
//     */
//    @SuppressWarnings("unchecked")
//    @Deprecated
//    public <T> T getTempVariableValue(final String key, final Class<T> clazz) {
//        return (T) tempVariablePool.get(key);
//    }
//
//    /**
//     * 获取基于key的key2的统计，用于两个list并为一个list，根据Key2不同计数
//     *
//     * @param key
//     *            分类key
//     * @param key2
//     *            计数key
//     * @return
//     */
//    @Deprecated
//    public int getAutoIndex(final String key, final String key2) {
//        @SuppressWarnings("unchecked")
//        List<String> indexs = (List<String>) tempVariablePool.get(key);
//        if (null == indexs) {
//            indexs = new ArrayList<String>();
//            tempVariablePool.put(key, indexs);
//        }
//        if (!indexs.contains(key2)) {
//            indexs.add(key2);
//        }
//        return indexs.size() - 1;
//    }
//
//    /**
//     * 用于结果集中追加一个节点，用于List类型节点，强制新增
//     *
//     * @param key
//     * @return
//     */
//    @Deprecated
//    public String generateAutoPlaceholderKey(String key) {
//        final String parent = MapUtils.getParentPath(key);
//        final String ak = MapUtils.generateKay(parent, Content.AUTO_PLACEHOLDER);
//        final String k = generateGlobalKey(ak);
//        final Map<String, Object> map = getResult(k);
//        map.put(Content.AUTO_PLACEHOLDER, null);// 用于新增节点点位属性，在后续Runner中会清除该类内容
//        return k;
//    }

    /**
     * 根据Key及当前的结果集构建全局的KEY，主要针对带有下标的key。结果集由三种类型，Map、TreeMap和叶子节点（非前两种类型），
     * Map表示普通节点，TreeMap表示带有下标的节点，通常两层结构，即TreeMap+Map，TreeMapkey为下标
     *
     * @param key
     * @return
     */
    @SuppressWarnings("unchecked")
    public String generateGlobalKey(String key) {
        if (StringUtils.isEmpty(key)) {
            return null;
        }
        if (!key.contains(Content.KEY_LIST_INDENTIFIER)) {// 如果Key中没有下标，则不需要重定位
            return key;
        }
        final String[] keys = MapUtils.splitKey(key);
        final StringBuilder sb = new StringBuilder();
        Map<String, Object> map = results;
        for (int i = 0; i < keys.length; i++) {// 循环遍历key路径
            final String k = keys[i];
            if (k.contains(Content.KEY_LIST_INDENTIFIER)) {// 节点包含下标
                final String[] ks = MapUtils.splitKey(k, Content.KEY_LIST_INDENTIFIER);//
                if (null == map) {// 如果map为null，表示还没有这部分结果，默认下标为0
                    sb.append(ks[0] + Content.KEY_LIST_INDENTIFIER + 0);
                } else {
                    // 带有下标的节点是由两部分组成，name+index,name是一个TreeMap，然后以index为key，value为Map
                    final Map<String, Object> value = (Map<String, Object>) map.get(ks[0]);// TreeMap,List节点
                    int index = 0;
                    if (null != value && !value.isEmpty()) {// 节点不为空
                        index = value.size() - 1;// TreeMap的size-1算出最后一个节点的key
                        final Object v = value.get(String.valueOf(index));// 取出Treemap中key最大的一个
                        if (isExist(v, keys, i + 1)) {// 判断下标是否需要增加
                            index += 1;
                            map = null;// 新建节点，后续的子节点都为空，下标初始为0
                        } else {
                            if (v instanceof Map) {
                                map = (Map<String, Object>) v;
                            } else {
                                map = null;
                            }
                        }
                    } else {
                        map = value;// 实际为null
                    }
                    sb.append(ks[0] + Content.KEY_LIST_INDENTIFIER + index);
                }
            } else {// 当前节点不包含下标
                sb.append(k);
                if (null != map) {// 当前节点不为空，取出下一级节点，继续遍历
                    final Object value = map.get(k);
                    if (value instanceof Map) {
                        map = (Map<String, Object>) value;
                    } else {// 下一级节点不为Map时，表示已经到叶子节点，下一节点为空
                        map = null;
                    }
                }
            }
            sb.append(Content.KEY_PATH_SPERALTOR);
        }
        String str = sb.toString();
        if (str.endsWith(Content.KEY_PATH_SPERALTOR)) {
            str = str.substring(0, str.length() - Content.KEY_PATH_SPERALTOR.length());
        }
        return str;
    }

    /**
     * 对带有下标的路径 进行判断，判断当前下标是否需要增加，需要遍历当前节点的后续节点是否存在，存在则增加
     *
     * @param object
     *            当前结果集节点
     * @param keys
     *            path路径数组
     * @param index
     *            当前路径 遍历的层级
     * @return
     */
    @SuppressWarnings("unchecked")
    private boolean isExist(Object object, String[] keys, int index) {
        if (null == object) {// 向后遍历时，如果节点为空表示节点不存在，不需要下标增加
            return false;
        }
        if (!(object instanceof Map)) {// 对象非空且不是Map子类型时，表示该节点已经存在，下标需要增加
            return true;
        }
        if (index >= keys.length) {// 处理最后一个节点为empty map情况 ，即已遍历完最后节点
            return false;
        }
        final Map<String, Object> map = (Map<String, Object>) object;
        final String key = keys[index];
        if (key.contains(Content.KEY_LIST_INDENTIFIER)) {
            // 在判断当前节点的下标是否需要增加时，向后遍历时， 如果遇到带有下标的节点，则返回false，即不需要增加，
            // 也可以说，下标是否增加是由节点下的叶子节点是否重复决定
            return false;
        } else {// 下一节点中不带有下标
            final Object obj = map.get(key);
            if (key.startsWith("&") && null == obj) {
                return true;
            }
            return isExist(obj, keys, index + 1);
        }
    }

//    @SuppressWarnings("unchecked")
//    @Deprecated
//    public <T> void addSaveResultTemp(T t) {
//        List<T> list = null;
//        if (tempVariablePool.containsKey("save_result")) {
//            list = (List<T>) tempVariablePool.get("save_result");
//        } else {
//            list = new ArrayList<T>();
//            addTempVariable("save_result", list);
//        }
//        if (!list.contains(t)) {
//            list.add(t);
//        }
//    }
//
//    @SuppressWarnings("unchecked")
//    @Deprecated
//    public <T> List<T> getSaveResultsFromTemp(Class<T> clazz) {
//        List<T> list = null;
//        if (tempVariablePool.containsKey("save_result")) {
//            list = (List<T>) tempVariablePool.get("save_result");
//        } else {
//            list = new ArrayList<T>();
//        }
//        return list;
//    }
//
//    @Deprecated
//    public void addCleaner(String clear) {
//        if (!StringUtils.isEmpty(clear)) {
//            if (null == cleaners) {
//                cleaners = new ArrayList<String>();
//            }
//            cleaners.add(clear);
//        }
//    }
//
//    /**
//     * @param cleaners
//     *            the cleaners to set
//     */
//    @Deprecated
//    public void setCleaners(List<String> cleaners) {
//        this.cleaners = cleaners;
//    }
//
//    /**
//     * @return the cleaners
//     */
//    @Deprecated
//    public List<String> getCleaners() {
//        return cleaners;
//    }
//
//    /**
//     * @param keyCleaner
//     *            the keyCleaner to set
//     */
//    @Deprecated
//    public void setKeyCleaner(String keyCleaner) {
//        this.keyCleaner = keyCleaner;
//    }
//
//    /**
//     * @return the keyCleaner
//     */
//    @Deprecated
//    public String getKeyCleaner() {
//        return keyCleaner;
//    }

    /**
     * @return the exceptionManager
     */
    public ExceptionManager getExceptionManager() {
        if (null == exceptionManager) {
            exceptionManager = new ExceptionManager();
        }
        return exceptionManager;
    }

    public void setContext(final MagicCubeContext context) {
        this.context = context;
    }

    public MagicCubeContext getContext() {
        return context;
    }

    public void setResultStrategy(final ResultStrategy resultStrategy) {
        this.resultStrategy = resultStrategy;
    }

    public MagicResponse response(){
        MagicResponse response = new MagicResponse();
        response.setResult(getResponseResult(true));
        response.setExceptionManager(getExceptionManager());
        return response;
    }
    /*
     * (non-Javadoc)
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {
        final Map<String, Object> map = MapUtils.treeMapToList(results);
        final String str = JsonUtils.toJson(map);
        return str;
    }
}
